/* 
 * Bitmain Tech, Home of Professional Bitmain Tech
 * Copyright 2014 Bitmain Tech Inc. and/or its affiliates and other contributors
 * as indicated by the @author tags. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 */
package com.bitmain.mmst.core.global.option;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

/**
 * properties配置文件加载工具，继承 {@link PropertyPlaceholderConfigurer} 实现，在spring中配置，见/config/spring/spring-core.xml
 *
 * @author Kweny - enkun.jiang@bitmain.com
 * @since 2015年12月11日
 *
 */
public class OptionConfigurer extends PropertyPlaceholderConfigurer {
	
	private static Logger logger = Logger.getLogger(OptionConfigurer.class);
	
	private String[] optionClassNames;
	
	/* (non-Javadoc)
	 * @see org.springframework.beans.factory.config.PropertyPlaceholderConfigurer#processProperties(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.Properties)
	 */
	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
			throws BeansException {
		super.processProperties(beanFactoryToProcess, props);
		buildOptions(props);
	}
	
	private void buildOptions(Properties props) {
		if (optionClassNames == null || optionClassNames.length == 0) {
			return;
		}
		for (String optionClassName : optionClassNames) {
			Class<?> optionClass = null;
			try {
				optionClass = Class.forName(optionClassName);
			} catch (Exception e) {
				logger.warn(e.getMessage(), e);
			}
			if (optionClass == null) {
				continue;
			}
			buildOption(props, optionClass);
		}
	}
	
	private void buildOption(Properties props, Class<?> optionClass) {
		Field[] fields = optionClass.getDeclaredFields();
		if (fields != null && fields.length > 0) {
			for (Field field : fields) {
				try {
					OptionKey annot = field.getAnnotation(OptionKey.class);
					if (annot == null) {
						continue;
					}
					field.setAccessible(true);
					try {
						Field modifiersField = Field.class.getDeclaredField("modifiers");
						modifiersField.setAccessible(true);
						modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
					} catch (Exception e) {
						e.printStackTrace();
					}
					String key = annot.value();
					String value = props.getProperty(key);
					if (value == null) {
						continue;
					}
					Class<?> fieldClass = field.getType();
					if (fieldClass == Byte.class) {
						try {
							field.set(this.getClass(), Byte.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Short.class) {
						try {
							field.set(this.getClass(), Short.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Integer.class) {
						try {
							field.set(this.getClass(), Integer.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Long.class) {
						try {
							field.set(this.getClass(), Long.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Float.class) {
						try {
							field.set(this.getClass(), Float.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Double.class) {
						try {
							field.set(this.getClass(), Double.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Boolean.class) {
						try {
							field.set(this.getClass(), Boolean.valueOf(value));
						} catch (Exception e) {
							e.printStackTrace();
						}
					} else if (fieldClass == Character.class) {
						try {
							field.set(this.getClass(), ((char) Integer.valueOf(value).intValue()));
						} catch (Exception e) {
							e.printStackTrace();
							try {
								field.set(this.getClass(), value.toCharArray()[0]);
							} catch (Exception e1) {
								e1.printStackTrace();
							}
						}
					} else if (fieldClass == String.class) {
						try {
							field.set(this.getClass(), value);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				} catch (Exception e) {
					StringBuilder log = new StringBuilder();
					log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n");
					log.append("\tOption build error.");
					log.append("\r\n\t!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\r\n");
					logger.error(log.toString(), e);
				}
			}
		}
	}
	
	/**
	 * @return the optionClassNames
	 */
	public String[] getOptionClassNames() {
		return optionClassNames;
	}

	/**
	 * @param optionClassNames the optionClassNames to set
	 */
	public void setOptionClassNames(String... optionClassNames) {
		this.optionClassNames = optionClassNames;
	}
}
